package ar.com.e2solver.solver.operator.impl;

import ar.com.e2solver.exception.BusinessException;
import ar.com.e2solver.model.Coordinate;
import ar.com.e2solver.model.Piece;
import ar.com.e2solver.model.Puzzle;
import ar.com.e2solver.solver.Individual;
import ar.com.e2solver.solver.RandomConstants;
import ar.com.e2solver.solver.operator.CrossoverOperator;

/**
 * Dado dos individuos crea un tercero por medio del intercambio 
 * random de fichas de las padres
 * @author rodo
 *
 */
public class SimpleCrossOverOperator implements CrossoverOperator {

	private static SimpleCrossOverOperator instance = new SimpleCrossOverOperator();
	
	private SimpleCrossOverOperator(){}
	
	public static SimpleCrossOverOperator getInstance(){
		return instance;
	}
	
	public Individual operate(Individual individual1, Individual individual2) throws BusinessException {
		// Crea una copia
		Puzzle newPuzzle1 = new Puzzle(individual1.getPuzzle());
		Puzzle newPuzzle2 = new Puzzle(individual2.getPuzzle());
		
		int pieces = newPuzzle1.getColumnNumbers() * newPuzzle1.getRowsNumber();
		for (int i = 0; i < pieces/6; i++) {
			doCrossOver(newPuzzle1, newPuzzle2);
		}
		
		return new Individual(newPuzzle1);
	}
	
	private static void doCrossOver(Puzzle newPuzzle1, Puzzle newPuzzle2) throws BusinessException {
		int rows = newPuzzle1.getRowsNumber();
		int cols = newPuzzle1.getColumnNumbers();
		int xRandom = RandomConstants.RANDOM.nextInt(rows);
		int yRandom = RandomConstants.RANDOM.nextInt(cols);
		
		Piece puzzle1PieceA = newPuzzle1.getPieceAt(xRandom, yRandom);
		Piece puzzle2PieceA = newPuzzle2.getPieceAt(xRandom, yRandom);
		
		// Busca la pieza que se corresponde con la pieza del otro puzzle
		Coordinate puzzle1PieceBCoordinate = newPuzzle1.findCoordinateForPiece(puzzle2PieceA);
		Piece puzzle1PieceB = newPuzzle1.getPieceAt(puzzle1PieceBCoordinate.getX(), puzzle1PieceBCoordinate.getY());
		
		// Intercambia las fichas. El intercambio es siempre con las fichas del mismo puzzle
		if(newPuzzle1.canSwap(xRandom, yRandom, puzzle1PieceBCoordinate.getX(), puzzle1PieceBCoordinate.getY())){
			
			newPuzzle1.swapPositions(xRandom, yRandom, puzzle1PieceBCoordinate.getX(), puzzle1PieceBCoordinate.getY());
			
			// Reacomoda las fichas si es que eran bordes o esquinas para que coincidan los bordes de su nueva posicion
			if(puzzle1PieceA.isABorderPiece()){
				rotateForBorder(xRandom, yRandom, puzzle1PieceB);
				rotateForBorder(puzzle1PieceBCoordinate.getX(), puzzle1PieceBCoordinate.getY(), puzzle1PieceA);
			
			} else if(puzzle1PieceA.isACornerPiece()){
				rotateForCorner(xRandom, yRandom, puzzle1PieceB);
				rotateForCorner(puzzle1PieceBCoordinate.getX(), puzzle1PieceBCoordinate.getY(), puzzle1PieceA);
			}
		}		
	}

	private static void rotateForBorder(int i, int j, Piece piece) {
		if(i == 0 ){
			while(!piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else if(i > j){
			while(!piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}	
		}
	}

	private static void rotateForCorner(int i, int j, Piece piece) {
		if(i == 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i == 0 && j != 0){
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i != 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		}
	}
}
